/**
 * @author Francesco Rosso, matr 592057
 */

package pcd1112.view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;

import pcd1112.model.Ambiente;
import pcd1112.model.Config;
import pcd1112.model.Coordinata;
import pcd1112.model.DescrizioneNodo;
import pcd1112.model.INodo;
import pcd1112.model.Nodo;

@SuppressWarnings("serial")
public class PannelloNodi extends JPanel {

	class PannelloInfoNodo extends JPanel implements Observer {
		private JTextField nodeId;
		private JTextField energy;
		private JTextField storedMessages;
		private JTextField receivedMessages;
		private JTextField sentMessages;
		private JTextField signatureChecks;
		private JTextField neighboursNumber;
		private JTextField position;

		private INodo currentNode = null;

		public PannelloInfoNodo() {
			super();
			setBorder(new LineBorder(new Color(0, 0, 0)));
			GridBagLayout gridBagLayout = new GridBagLayout();
			gridBagLayout.columnWidths = new int[] { 115, 120, 0 };
			gridBagLayout.rowHeights = new int[] { 22, 22, 22, 22, 22, 22, 22,
					22, 0 };
			gridBagLayout.columnWeights = new double[] { 0.0, 0.0,
					Double.MIN_VALUE };
			gridBagLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0,
					0.0, 0.0, 0.0, Double.MIN_VALUE };
			setLayout(gridBagLayout);

			JLabel lblNodeId = new JLabel("Node ID");
			lblNodeId.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblNodeId = new GridBagConstraints();
			gbc_lblNodeId.anchor = GridBagConstraints.EAST;
			gbc_lblNodeId.insets = new Insets(0, 0, 5, 5);
			gbc_lblNodeId.gridx = 0;
			gbc_lblNodeId.gridy = 0;
			add(lblNodeId, gbc_lblNodeId);

			nodeId = new JTextField();
			GridBagConstraints gbc_nodeId = new GridBagConstraints();
			gbc_nodeId.anchor = GridBagConstraints.NORTH;
			gbc_nodeId.fill = GridBagConstraints.HORIZONTAL;
			gbc_nodeId.insets = new Insets(0, 0, 5, 0);
			gbc_nodeId.gridx = 1;
			gbc_nodeId.gridy = 0;
			add(nodeId, gbc_nodeId);
			nodeId.setColumns(10);

			JLabel lblEnergy = new JLabel("Residue energy");
			lblEnergy.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblEnergy = new GridBagConstraints();
			gbc_lblEnergy.anchor = GridBagConstraints.EAST;
			gbc_lblEnergy.insets = new Insets(0, 0, 5, 5);
			gbc_lblEnergy.gridx = 0;
			gbc_lblEnergy.gridy = 1;
			add(lblEnergy, gbc_lblEnergy);

			energy = new JTextField();
			GridBagConstraints gbc_energy = new GridBagConstraints();
			gbc_energy.anchor = GridBagConstraints.NORTH;
			gbc_energy.fill = GridBagConstraints.HORIZONTAL;
			gbc_energy.insets = new Insets(0, 0, 5, 0);
			gbc_energy.gridx = 1;
			gbc_energy.gridy = 1;
			add(energy, gbc_energy);
			energy.setColumns(10);

			JLabel lblStoredMessages = new JLabel("Stored messages");
			lblStoredMessages.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblStoredMessages = new GridBagConstraints();
			gbc_lblStoredMessages.anchor = GridBagConstraints.EAST;
			gbc_lblStoredMessages.insets = new Insets(0, 0, 5, 5);
			gbc_lblStoredMessages.gridx = 0;
			gbc_lblStoredMessages.gridy = 2;
			add(lblStoredMessages, gbc_lblStoredMessages);

			storedMessages = new JTextField();
			GridBagConstraints gbc_storedMessages = new GridBagConstraints();
			gbc_storedMessages.anchor = GridBagConstraints.NORTH;
			gbc_storedMessages.fill = GridBagConstraints.HORIZONTAL;
			gbc_storedMessages.insets = new Insets(0, 0, 5, 0);
			gbc_storedMessages.gridx = 1;
			gbc_storedMessages.gridy = 2;
			add(storedMessages, gbc_storedMessages);
			storedMessages.setColumns(10);

			JLabel lblReceivedMessages = new JLabel("Received messages");
			lblReceivedMessages.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblReceivedMessages = new GridBagConstraints();
			gbc_lblReceivedMessages.anchor = GridBagConstraints.EAST;
			gbc_lblReceivedMessages.insets = new Insets(0, 0, 5, 5);
			gbc_lblReceivedMessages.gridx = 0;
			gbc_lblReceivedMessages.gridy = 3;
			add(lblReceivedMessages, gbc_lblReceivedMessages);

			receivedMessages = new JTextField();
			GridBagConstraints gbc_receivedMessages = new GridBagConstraints();
			gbc_receivedMessages.anchor = GridBagConstraints.NORTH;
			gbc_receivedMessages.fill = GridBagConstraints.HORIZONTAL;
			gbc_receivedMessages.insets = new Insets(0, 0, 5, 0);
			gbc_receivedMessages.gridx = 1;
			gbc_receivedMessages.gridy = 3;
			add(receivedMessages, gbc_receivedMessages);
			receivedMessages.setColumns(10);

			JLabel lblSentMessages = new JLabel("Sent messages");
			lblSentMessages.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblSentMessages = new GridBagConstraints();
			gbc_lblSentMessages.anchor = GridBagConstraints.EAST;
			gbc_lblSentMessages.insets = new Insets(0, 0, 5, 5);
			gbc_lblSentMessages.gridx = 0;
			gbc_lblSentMessages.gridy = 4;
			add(lblSentMessages, gbc_lblSentMessages);

			sentMessages = new JTextField();
			GridBagConstraints gbc_sentMessages = new GridBagConstraints();
			gbc_sentMessages.anchor = GridBagConstraints.NORTH;
			gbc_sentMessages.fill = GridBagConstraints.HORIZONTAL;
			gbc_sentMessages.insets = new Insets(0, 0, 5, 0);
			gbc_sentMessages.gridx = 1;
			gbc_sentMessages.gridy = 4;
			add(sentMessages, gbc_sentMessages);
			sentMessages.setColumns(10);

			JLabel lblSignatureChecks = new JLabel("Signature checks");
			lblSignatureChecks.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblSignatureChecks = new GridBagConstraints();
			gbc_lblSignatureChecks.anchor = GridBagConstraints.EAST;
			gbc_lblSignatureChecks.insets = new Insets(0, 0, 5, 5);
			gbc_lblSignatureChecks.gridx = 0;
			gbc_lblSignatureChecks.gridy = 5;
			add(lblSignatureChecks, gbc_lblSignatureChecks);

			signatureChecks = new JTextField();
			GridBagConstraints gbc_signatureChecks = new GridBagConstraints();
			gbc_signatureChecks.anchor = GridBagConstraints.NORTH;
			gbc_signatureChecks.fill = GridBagConstraints.HORIZONTAL;
			gbc_signatureChecks.insets = new Insets(0, 0, 5, 0);
			gbc_signatureChecks.gridx = 1;
			gbc_signatureChecks.gridy = 5;
			add(signatureChecks, gbc_signatureChecks);
			signatureChecks.setColumns(10);

			JLabel lblNeighboursNumber = new JLabel("Neighbour number");
			lblNeighboursNumber.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblNeighboursNumber = new GridBagConstraints();
			gbc_lblNeighboursNumber.anchor = GridBagConstraints.EAST;
			gbc_lblNeighboursNumber.insets = new Insets(0, 0, 5, 5);
			gbc_lblNeighboursNumber.gridx = 0;
			gbc_lblNeighboursNumber.gridy = 6;
			add(lblNeighboursNumber, gbc_lblNeighboursNumber);

			neighboursNumber = new JTextField();
			GridBagConstraints gbc_neighboursNumber = new GridBagConstraints();
			gbc_neighboursNumber.anchor = GridBagConstraints.NORTH;
			gbc_neighboursNumber.fill = GridBagConstraints.HORIZONTAL;
			gbc_neighboursNumber.insets = new Insets(0, 0, 5, 0);
			gbc_neighboursNumber.gridx = 1;
			gbc_neighboursNumber.gridy = 6;
			add(neighboursNumber, gbc_neighboursNumber);
			neighboursNumber.setColumns(10);

			JLabel lblPositionxY = new JLabel("Position (x, y)");
			lblPositionxY.setHorizontalAlignment(SwingConstants.RIGHT);
			GridBagConstraints gbc_lblPositionxY = new GridBagConstraints();
			gbc_lblPositionxY.anchor = GridBagConstraints.EAST;
			gbc_lblPositionxY.insets = new Insets(0, 0, 0, 5);
			gbc_lblPositionxY.gridx = 0;
			gbc_lblPositionxY.gridy = 7;
			add(lblPositionxY, gbc_lblPositionxY);

			position = new JTextField();
			GridBagConstraints gbc_position = new GridBagConstraints();
			gbc_position.anchor = GridBagConstraints.NORTH;
			gbc_position.fill = GridBagConstraints.HORIZONTAL;
			gbc_position.gridx = 1;
			gbc_position.gridy = 7;
			add(position, gbc_position);
			position.setColumns(10);
		}

		public void setNode(Nodo nodo) {
			if (currentNode != null)
				((Nodo) currentNode).deleteObserver(this);
			if (nodo != null) {
				currentNode = nodo;
				nodo.addObserver(this);
				update();
			}
		}

		private void update() {
			DescrizioneNodo desc = currentNode.getStato();
			nodeId.setText(Integer.toString(desc.getIdNodo()));
			energy.setText(Integer.toString(desc.getEnergiaRimasta()));
			storedMessages.setText(Integer.toString(desc
					.getNumMessaggiMemorizzati()));
			sentMessages
					.setText(Integer.toString(desc.getNumMessaggiSpediti()));
			receivedMessages.setText(Integer.toString(desc
					.getNumMessaggiRicevuti()));
			signatureChecks.setText(Integer.toString(desc
					.getNumSignatureChecks()));
			neighboursNumber.setText(Integer.toString(desc.getNumVicini()));
			position.setText(desc.getPosizione().toString());
		}

		@Override
		public void update(Observable o, Object arg) {
			String response = (String) arg;
			if (response.equals("MessageElaborated")) {
				update();
			}
			if (response.equals("TerminatedNode")) {
				reset();
			}
		}

		private void reset() {
			nodeId.setText("");
			energy.setText("");
			storedMessages.setText("");
			receivedMessages.setText("");
			sentMessages.setText("");
			signatureChecks.setText("");
			neighboursNumber.setText("");
			position.setText("");
			setNode(null);
		}
	}

	private class CanvasNodi extends JPanel implements MouseListener {

		private INodo nodoSelezionato = new Nodo(new Coordinata(0, 0), 0, 0);
		private Color selected, normal, clone, cloned, selectedArea,
				normalArea, cloneArea, clonedArea;
		private PannelloInfoNodo infoNodo;

		public CanvasNodi(PannelloInfoNodo pan) {
			super();
			this.addMouseListener(this);
			infoNodo = pan;
			setSize(300, 300);
			normal = new Color(0, 0, 153);
			normalArea = new Color(0, 102, 255, 50);

			selected = new Color(0, 102, 51);
			selectedArea = new Color(51, 204, 51, 100);

			clone = new Color(153, 0, 0);
			cloneArea = new Color(204, 51, 51, 150);

			cloned = new Color(255, 51, 0);
			clonedArea = new Color(255, 255, 102, 150);
		}

		public void paintComponent(Graphics g) {
			super.paintComponent(g);

			Graphics2D nodeArea = (Graphics2D) g;

			nodeArea.setColor(Color.blue);

			Dimension size = getSize();
			Insets insets = getInsets();

			// We want a square and we select the min dimension as side
			if (size.width > size.height) {
				size.width = size.height;
			} else {
				size.height = size.width;
			}

			setSize(size);

			int w = size.width - insets.left - insets.right;
			int h = size.height - insets.top - insets.bottom;

			Ambiente a = Ambiente.getAmbiente();
			Coordinata pos;
			Ellipse2D.Double areaComunicazione;
			Ellipse2D.Double bordoComunicazione;
			Ellipse2D.Double nodo;
			double x, y;
			double raggio = Config.getConfig().getR();
			ArrayList<INodo> nodi = a.getNodi();
			for (int i = 0; i < nodi.size(); i++) {
				pos = nodi.get(i).getPosizione();
				x = Math.abs(pos.getX() * w);
				y = Math.abs(pos.getY() * h);
				areaComunicazione = new Ellipse2D.Double(x - (raggio * w), y
						- (raggio * h), raggio * 2 * w, raggio * 2 * h);
				bordoComunicazione = new Ellipse2D.Double(x - (raggio * w), y
						- (raggio * h), raggio * 2 * w, raggio * 2 * h);
				nodo = new Ellipse2D.Double(x - (2), y - (2), 2 * 2, 2 * 2);

				if (nodoSelezionato.equals(nodi.get(i))) {
					nodeArea.setColor(selected);
					nodeArea.fill(nodo);
					nodeArea.draw(bordoComunicazione);
					nodeArea.setColor(selectedArea);
					nodeArea.fill(areaComunicazione);
				} else {
					nodeArea.setColor(normal);
					nodeArea.fill(nodo);
					nodeArea.draw(bordoComunicazione);
					nodeArea.setColor(normalArea);
					nodeArea.fill(areaComunicazione);
				}
				if (nodi.get(i).isClone()) {
					nodeArea.setColor(clone);
					nodeArea.fill(nodo);
					nodeArea.draw(bordoComunicazione);
					nodeArea.setColor(cloneArea);
					nodeArea.fill(areaComunicazione);

				}
				if (nodi.get(i).isCloned()) {
					nodeArea.setColor(cloned);
					nodeArea.fill(nodo);
					nodeArea.draw(bordoComunicazione);
					nodeArea.setColor(clonedArea);
					nodeArea.fill(areaComunicazione);

				}
			}
		}

		@Override
		public void setLayout(LayoutManager mgr) {
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			if ((e.getButton() == 1)) {
				Dimension size = getSize();
				Insets insets = getInsets();
				double w = size.width - insets.left - insets.right;
				double h = size.height - insets.top - insets.bottom;
				Coordinata puntoSelezionato = new Coordinata(e.getX() / w,
						e.getY() / h);
				ArrayList<INodo> nodi = Ambiente.getAmbiente().getNodi();
				boolean trovato = false;
				for (int i = 0; i < nodi.size() && !trovato; i++) {
					// if the specified point is in the nodi.get(i)'s area and
					// it's a node we
					// haven't clicked before (useful when there are a lot of
					// nodes) in the same point
					if (puntoSelezionato.compresaInArea(nodi.get(i)
							.getPosizione(), Config.getConfig().getR())
							&& !nodi.get(i).equals(nodoSelezionato)) {
						nodoSelezionato = nodi.get(i);
						trovato = true;
					}
				}

				infoNodo.setNode((Nodo) nodoSelezionato);

				repaint();

			}
		}

		@Override
		public void mousePressed(MouseEvent e) {
			// Auto-generated method stub

		}

		@Override
		public void mouseReleased(MouseEvent e) {
			// Auto-generated method stub

		}

		@Override
		public void mouseEntered(MouseEvent e) {
			// Auto-generated method stub

		}

		@Override
		public void mouseExited(MouseEvent e) {
			// Auto-generated method stub

		}

	}

	private PannelloInfoNodo infoNodo = new PannelloInfoNodo();

	private CanvasNodi nodi = new CanvasNodi(infoNodo);

	public PannelloNodi() {
		super();
		setLayout(new GridLayout(1, 2));
		add(nodi);
		add(infoNodo);
	}

	public void aggiorna() {
		nodi.repaint();
	}

}
